Android AsyncTask的理解

AsyncTask可以正确,轻松地使用UI线程。 此类允许您执行后台操作并在UI线程上发布结果,而无需操纵Threads或者Handler。

AsyncTask被设计为围绕Thread和Handler的帮助类,并不是一个通用的线程框架。 理想情况下,AsyncTasks应该用于短操作(最多几秒钟)。如果您需要长时间保持线程运行,强烈建议您使用java.util.concurrent包提供的各种API。 例如ExecutorThreadPoolExecutorFutureTask

From Google的官方文档

1、AsyncTask的使用

AsyncTask 用来在后台线程中执行任务,当任务执行完毕之后将结果发送到主线程当中。它有三个重要的泛类型参数,分别是 ParamsProgressResult,分别用来指定参数、进度和结果的值的类型。 以及四个重要的方法,分别是 onPreExecute(), doInBackground(), onProgressUpdate()onPostExecute()。 这四个方法中,除了 doInBackground(),其他三个都是运行在UI线程的,分别用来处理在任务开始之前、任务进度改变的时候以及任务执行完毕之后的逻辑,而 doInBackground() 运行在后台线程中,用来执行耗时的任务。

1.1、典型的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class AsyncTaskActivity extends BaseActivity {

private ProgressBar progressBar;

private ImageView imageView;

private TextView textView;

private Button button;

private final String url = "https://upload-images.jianshu.io/upload_images/2177145-33c7b5ff75cf2bf7.png";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);

imageView = findViewById(R.id.demo_image);
textView = findViewById(R.id.demo_showProgress);
button = findViewById(R.id.demo_button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
imageView.setImageBitmap(null);
new MyTask().execute(url);
}
});
}

private class MyTask extends AsyncTask<String, String, Bitmap> {

@Override
protected void onPreExecute() {
super.onPreExecute();
textView.setText("异步开始");
}

@Override
protected Bitmap doInBackground(String... strings) {
publishProgress("正在下载图片");
return getBitmapFromUrl(strings[0]);
}

@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
textView.setText(values[0]);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
imageView.setImageBitmap(bitmap);
textView.setText("下载结束");
}

}


public Bitmap getBitmapFromUrl(String urlString) {
Bitmap bitmap;
InputStream is = null;

try {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(connection.getInputStream());
bitmap = BitmapFactory.decodeStream(is);
connection.disconnect();
return bitmap;

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

1.2、非典型使用(并发)

1
2
// 替换1.1中的25 Line,因为只有一个url,此处仅是举例,无实际意义
new MyTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);

使用AsyncTask的时候要注意以下几点内容:

  1. AsyncTask 的对象必须在主线程中创建;
  2. execute() 方法必须在UI线程中被调用;
  3. 不要直接调用 onPreExecute(), doInBackground(), onProgressUpdate()onPostExecute()
  4. 一个AsyncTask对象的 execute() 方法只能被调用一次;

2、AsyncTask源码分析

2.1、AsyncTask 的初始化过程

当初始化一个 AsyncTask 的时候,所有的重载构造方法都会调用下面的这个构造方法。这里做了几件事情:

  1. 初始化一个 Handler 对象 mHandler,该 Handler 用来将消息发送到它所在的线程中,通常使用默认的值,即主线程的 Handler;
  2. 初始化一个 WorkerRunnable 对象 mWorker。它是一个 WorkerRunnable 类型的实例,而 WorkerRunnable 又继承自 Callable,因此它是一个可以被执行的对象。我们会把在该对象中回调 doInBackground() 来将我们的业务逻辑放在线程池中执行。
  3. 初始化一个 FutureTask 对象 mFuture。该对象包装了 mWorker 并且当 mWorker 执行完毕之后会调用它的 postResultIfNotInvoked() 方法来通知主线程(不论任务已经执行完毕还是被取消了,都会调用这个方法)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public AsyncTask(@Nullable Looper callbackLooper) {
// 1. 初始化用来发送消息的 Handler
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);

// 2. 封装一个对象用来执行我们的任务
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 回调我们的业务逻辑
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 发送结果给主线程
postResult(result);
}
return result;
}
};

// 3. 初始化一个 FutureTask,并且当它执行完毕的时候,会调用 postResultIfNotInvoked 来将消息的执行结果发送到主线程中
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
// 如果任务没有被触发,也要发送一个结果
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}

当这样设置完毕之后,我们就可以使用 execute() 方法来开始执行任务了。

2.2、AsyncTask 中任务的串行执行过程

我们从 execute() 方法开始分析 AsyncTask,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
if (mStatus != Status.PENDING) { // 1.判断线程当前的状态
switch (mStatus) {
case RUNNING: throw new IllegalStateException(...);
case FINISHED: throw new IllegalStateException(...);
}
}
mStatus = Status.RUNNING;
onPreExecute(); // 2.回调生命周期方法
mWorker.mParams = params; // 3.赋值给可执行的对象 WorkerRunnable
exec.execute(mFuture); // 4.在线程池中执行任务
return this;
}

当我们调用 AsyncTask 的 execute() 方法的时候会立即调用它的 executeOnExecutor() 方法。这里传入了两个参数,分别是一个 Executor 和任务的参数 params。从上面我们可以看出,当直接调用 execute() 方法的时候会使用默认的线程池 sDefaultExecutor,而当我们指定了线程池之后,会使用我们指定的线程池来执行任务。

在 1 处,会对 AsyncTask 当前的状态进行判断,这就对应了前面说的,一个任务只能被执行一次。在 2 处会调用 onPreExecute() 方法,如果我们覆写了该方法,那么它就会在这个时候被调用。在 3 处的操作是在为 mWorker 赋值,即把调用 execute 方法时传入的参数赋值给了 mWorker。接下来,会将 mFuture 添加到线程池中执行。

当我们不指定任何线程池的时候使用的 sDefaultExecutor 是一个串行的线程池,它的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;

public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
// 相当于对传入的Runnable进行了一层包装
r.run();
} finally {
// 分配下一个任务
scheduleNext();
}
}
});
// 如果当前没有正在执行的任务,那么就尝试从队列中取出并执行
if (mActive == null) {
scheduleNext();
}
}

protected synchronized void scheduleNext() {
// 从队列中取任务并使用THREAD_POOL_EXECUTOR执行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}

从上面我们可以看出,我们添加到线程池中的任务实际上并没有直接交给线程池来执行,而是对其进行了处理之后才执行的,SerialExecutor 通过内部维护了双端队列ArrayDeque,每当一个 AsyncTask 调用 execute() 方法的时候都会被放在该队列当中进行排队。如果当前没有正在执行的任务,那么就从队列中取一个任务交给 THREAD_POOL_EXECUTOR 执行;当一个任务执行完毕之后又会调用 scheduleNext() 取下一个任务执行。也就是说,实际上 sDefaultExecutor 在这里只是起了一个任务调度的作用,任务最终还是交给 THREAD_POOL_EXECUTOR 执行的。这里的THREAD_POOL_EXECUTOR也是一个线程池,它在静态代码块中被初始化:

1
2
3
4
5
6
7
8
static {
// 使用指定的参数创建一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

2.3、将任务执行的结果发送到其他线程

上面的 WorkerRunnable 中已经用到了 postResult 方法,它用来将任务执行的结果发送给 Handler

1
2
3
4
5
6
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}

mHandler 会在创建 AsyncTask 的时候初始化。我们可以通过 AsyncTask 的构造方法传入 Handler 和 Looper 来指定该对象所在的线程。当我们没有指定的时候,会使用 AsyncTask 内部的 InternalHandler 创建 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private final Handler mHandler;

public AsyncTask(@Nullable Looper callbackLooper) {
// 根据传入的参数创建Handler对象
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler() : new Handler(callbackLooper);
}

private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
// 使用 InternalHandler 创建对象
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}

// AsyncTask 内部定义 的Handler 类型
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}

@Override public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
// 根据传入的消息类型进行处理
switch (msg.what) {
case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break;
case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break;
}
}
}

3、总结

上面我们梳理了 AsyncTask 的大致过程,我们来梳理下:

创建 AsyncTask是会创建mWorker和mFuture,mFuture是一个Runable类型的对象,最终会在我们执行execute —> executeOnExecutor时,先调用了onPreExecute()方法,然后将mFuture传入执行队列中等待执行。这里存在两个线程池,一个是 SerialExecutor,一个是 THREAD_POOL_EXECUTOR,前者主要用来进行任务调度,即把交给线程的任务放在队列中进行排队执行,而实际上所有的任务都是在后者中执行完成的。在mFuture被执行的时候,会回调mWorker的call方法,call方法里会调用doInBackground方法,获得doInBackground的执行结果后调用postResult方法,postResult内部通过handler切换线程,最终调用我们的finish方法,finish里面会调用onCancelled(result)或者onPostExecute(result);中的一个。这样我们的AsyncTask的一个关键流程就走完了。

4、参考